package fr.jade.fraclite.deployment;

import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.objectweb.fractal.api.Component;
import org.objectweb.fractal.api.control.IllegalBindingException;
import org.objectweb.fractal.deployment.local.api.IModule;
import org.objectweb.fractal.deployment.local.api.IModuleDescriptor;
import org.objectweb.fractal.deployment.local.api.IPackageExport;
import org.objectweb.fractal.deployment.local.api.IPhysicalPackage;
import org.objectweb.fractal.deployment.local.api.ModuleComponent;

public class GenericModuleComponent extends ModuleComponent implements IModule {

	Component thisComponent = null;

	Map<String, IPackageExport> exportedSoftwarePackages = null;

	Map<String, IPhysicalPackage> physicalPackages = null;

	public GenericModuleComponent() {
		this.moduleClassLoader = new ModuleURLClassLoader(new URL[] {});

		exportedSoftwarePackages = new HashMap<String, IPackageExport>();
		physicalPackages = new HashMap<String, IPhysicalPackage>();
	}

	public String[] listFc() {
		List resultList = new ArrayList(importedSoftwarePackages.keySet());
		resultList.addAll(physicalPackages.keySet());

		return (String[]) resultList.toArray(new String[resultList.size()]);
	}

	public Object lookupFc(String itfName) {
		if (itfName.startsWith("physicalPackage")) {
			return physicalPackages.get(itfName);
		}
		return importedSoftwarePackages.get(itfName);
	}

	public void bindFc(String itfName, Object itfObject)
			throws IllegalBindingException {
		if (itfName.equals("component")) {
			thisComponent = (Component) itfObject;
		} else if (itfName.startsWith("physicalPackage")) {
			physicalPackages.put(itfName, (IPhysicalPackage) itfObject);
			updateModuleClassLoader();
		} else {
			super.bindFc(itfName, itfObject);
		}
	}

	public void unbindFc(String itfName) {
		if (itfName.startsWith("physicalPackage")) {
			physicalPackages.remove(itfName);
			updateModuleClassLoader();
		} else {
			super.unbindFc(itfName);
		}
	}

	public Object queryInterface(String interfaceName) {
		if (!exportedSoftwarePackages.containsKey(interfaceName)) {
			IPackageExport result = new BasicPackage(interfaceName, this);
			exportedSoftwarePackages.put(interfaceName, result);
			return result;
		} else {
			return exportedSoftwarePackages.get(interfaceName);
		}
	}

	public Object queryInterface(String interfaceName, String interfaceSignature) {
		if (interfaceSignature
				.equals("org.objectweb.fractal.deployment.local.api.IModule")) {
			return this;
		} else {
			return queryInterface(interfaceName);
		}
	}

	@Override
	public IModuleDescriptor getPackageDescription() {
		return null;
	}

	@Override
	public boolean isMarked() {
		return false;
	}

	private class BasicPackage implements IPackageExport {

		private String packageName;

		private GenericModuleComponent module = null;

		public BasicPackage(String packageName, GenericModuleComponent comp) {
			this.packageName = packageName;
			this.module = comp;
		}

		public URL getResource(String resourceName) {
			return module.getResource(packageName + "." + resourceName);
		}

		public InputStream getResourceAsStream(String resourceName) {
			return module.getResourceAsStream(packageName + "." + resourceName);
		}

		public Class loadClass(String className) throws ClassNotFoundException {
			return module.loadClass(packageName + "." + className);
		}

		public boolean equals(Object o) {
			boolean result = false;
			if (o instanceof BasicPackage) {
				BasicPackage comparedPackage = (BasicPackage) o;
				if (this.packageName.equals(comparedPackage.packageName)
						&& this.module.equals(comparedPackage.module)) {
					result = true;
				}
			}
			return result;
		}
	}

	public ClassLoader getClassLoader() {
		return moduleClassLoader;
	}

	public URL getResource(String resourceName) {
		return moduleClassLoader.getResource(resourceName);
	}

	public InputStream getResourceAsStream(String resourceName) {
		return moduleClassLoader.getResourceAsStream(resourceName);
	}

	public Class loadClass(String qualifiedClassName)
			throws ClassNotFoundException {
		return moduleClassLoader.loadClass(qualifiedClassName);
	}

	private void updateModuleClassLoader() {
		Collection<IPhysicalPackage> packages = physicalPackages.values();
		Set<URL> urlSet = new HashSet<URL>();
		Iterator<IPhysicalPackage> it = packages.iterator();
		while (it.hasNext()) {
			IPhysicalPackage physPkg = (IPhysicalPackage) it.next();
			((ModuleURLClassLoader) this.moduleClassLoader).addURL(physPkg
					.getContent());
		}
	}

	private class ModuleURLClassLoader extends URLClassLoader {

		public ModuleURLClassLoader(URL[] urls) {
			super(urls);
		}

		public Class loadClass(String qualifiedClassName)
				throws ClassNotFoundException {
			Class result = null;

			String pkgName = qualifiedClassName.substring(0, qualifiedClassName
					.lastIndexOf('.'));
			String className = qualifiedClassName.substring(qualifiedClassName
					.lastIndexOf('.') + 1);

			IPackageExport exporter = importedSoftwarePackages.get(pkgName);
			if (exporter != null) {
				result = exporter.loadClass(className);
			} else {
				result = super.loadClass(qualifiedClassName);
			}

			return result;
		}

		public URL getResource(String resourceName) {
			URL result = importedSoftwarePackages.get(resourceName)
					.getResource(resourceName);
			if (result == null) {
				result = super.getResource(resourceName);
			}
			return result;
		}

		public InputStream getResourceAsStream(String resourceName) {
			InputStream result = importedSoftwarePackages.get(resourceName)
					.getResourceAsStream(resourceName);
			if (result == null) {
				result = super.getResourceAsStream(resourceName);
			}
			return result;
		}

		public void addURL(URL url) {
			super.addURL(url);
		}
	}
}
